<?php

declare(strict_types=1);

namespace App\Helpers;

// use App\Helpers\Code;

use App\Model\Setting\SettingAppHidden;
use App\Model\User;
use App\Model\UserExcludeTip;
use App\Model\UserProfile;
use Psr\Log\LoggerInterface;
use \Exception;
use GuzzleHttp\Client;
use Hyperf\Amqp\Message\ProducerMessage;
use Hyperf\Guzzle\HandlerStackFactory;
use Hyperf\Utils\ApplicationContext;
// use HyperfLibraries\Sms\Contract\SmsInterface;
use Hyperf\Utils\Context;
use Psr\Http\Message\ServerRequestInterface;
use Hyperf\Amqp\Producer;
use Hyperf\AsyncQueue\Driver\DriverFactory;

class Helper
{
    public function result($code, $message, $data)
    {
        return ['code' => $code, 'message' => $message, 'data' => $data];
    }

    public function jsonEncode($data)
    {
        return json_encode($data, JSON_UNESCAPED_UNICODE);
    }

    /**
     * 生成随机数
     * @param number $length
     * @return number
     */
    public function generateNumber($length = 6)
    {
        return rand(pow(10, ($length - 1)), pow(10, $length) - 1);
    }

    /**
     * 生成随机字符串
     * @param number $length
     * @param string $chars
     * @return string
     */
    public function generateString($length = 6, $chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz')
    {
        $chars = str_split($chars);

        $chars = array_map(function ($i) use ($chars) {
            return $chars[$i];
        }, array_rand($chars, $length));

        return implode($chars);
    }

    /**
     * xml to array 转换
     * @param type $xml
     * @return type
     */
    public function xml2array($xml)
    {
        return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
    }

    public static function getLogger($name = 'app', $group = "app"): LoggerInterface
    {
        return \Hyperf\Utils\ApplicationContext::getContainer()->get(\Hyperf\Logger\LoggerFactory::class)->get($name, $group);
    }

    /**
     * 是否开发环境
     *
     * @return boolean
     */
    public static function is_dev()
    {
        return in_array(env('APP_ENV', 'dev'), ['dev', 'local', 'test']);
    }

    /**
     * 是否调试模式
     *
     * @return boolean
     */
    public static function is_debug()
    {
        return env('APP_DEBUG', 'debug') != 'production';
    }

    /**
     * 获取验证码
     */
    static function getCaptcha($phone = '')
    {
        $captchaKey = 'Captcha_' . $phone;
        $cacheTime = 300;
        return RedisUtil::get($captchaKey, function () {
            return StringUtil::getIntCaptcha(4);
        }, $cacheTime);
    }

    /**
     * 验证码检测
     */
    static function checkCaptcha($phone = '', $captcha = '')
    {
        $captchaKey = 'Captcha_' . $phone;
        if ($captcha != RedisUtil::get($captchaKey)) return false;
        return true;
    }

    /**
     * 发送验证码
     */
    static function sendCaptcha($phone = '', $captcha = '', $appId = 1,$curChannel='')
    {
        $appName = ConfigUtil::getAppName($appId,$curChannel);
        try {
            $smsUtil = new SmsWelinkUtil();
            $ret = $smsUtil->send($phone, "【{$appName}】您的验证码{$captcha}，该验证码5分钟内有效，请勿泄漏于他人！");
            log_user_result($phone, "Ret:{$ret}");
            $ret = json_decode($ret, true);
            if (isset($ret['Result']) && $ret['Result'] == 'succ') return ['result' => 1, 'result_info' => $ret];
            else return ['result' => 0, 'result_info' => $ret];
            // } catch (\HyperfLibraries\Sms\Exception\NoGatewayAvailableException $exception) {
        } catch (Exception $exception) {
            log_info(__CLASS__ . "==Exception==" . json_encode($exception->getMessage()));
        }
        return;
    }

    /**
     * 验证码-创蓝
     */
    static function sendClCaptcha($phone = '', $captcha = '', $appId = 1)
    {
        $appName = ConfigUtil::getAppName($appId);
        try {
            $smsUtil = new SmsChuanglanUtil();
            $ret = $smsUtil->send($phone, "【{$appName}】您的验证码{$captcha}，该验证码5分钟内有效，请勿泄漏于他人！", $captcha);
            log_user_result($phone, "Ret:{$ret}");
            $ret = json_decode($ret, true);
            if (isset($ret['code']) && $ret['code'] == '0') return ['result' => 1, 'result_info' => $ret];
            else return ['result' => 0, 'result_info' => $ret];
        } catch (Exception $exception) {
            log_info(__CLASS__ . "==Exception==" . json_encode($exception->getMessage()));
        }
        return;
    }

    /**
     * 获取客户端IP
     */
    static function getClientIp()
    {
        $request = Context::get(ServerRequestInterface::class);
        $ip_addr = $request->getHeaderLine('x-forwarded-for');
        if (self::verifyIp($ip_addr)) {
            return $ip_addr;
        }
        $ip_addr = $request->getHeaderLine('remote-host');
        if (self::verifyIp($ip_addr)) {
            return $ip_addr;
        }
        $ip_addr = $request->getHeaderLine('x-real-ip');
        if (self::verifyIp($ip_addr)) {
            return $ip_addr;
        }
        $ip_addr = $request->getServerParams()['remote_addr'] ?? '0.0.0.0';
        if (self::verifyIp($ip_addr)) {
            return $ip_addr;
        }
        return '0.0.0.0';
    }

    /**
     * 验证IP
     */
    static function verifyIp($realip)
    {
        return filter_var($realip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4);
    }

    /**
     * 根据时间戳显示时间
     * @param int $timestamp
     */
    static function showOnlineTime($timestamp = 0)
    {
        $timestamp = $timestamp ? strtotime($timestamp) : time(); //is_string($timestamp) ? strtotime($timestamp) : $timestamp;
        $offtime = time() - $timestamp;
        if ($offtime > 7 * 24 * 3600) return date('m/d', $timestamp);
        elseif ($offtime > 24 * 3600) return floor($offtime / (24 * 3600)) . '天前';
        elseif ($offtime > 3600) return floor($offtime / 3600) . '小时前';
        elseif ($offtime > 8 * 60) return floor($offtime / 60) . '分钟前';
        return '1分钟内';
    }

    /**
     * 重写实现 http_build_query 提交实现(同名key)key=val1&key=val2
     * @param array $formData 数据数组
     * @param string $numericPrefix 数字索引时附加的Key前缀
     * @param string $argSeparator 参数分隔符(默认为&)
     * @param string $prefixKey Key 数组参数，实现同名方式调用接口
     * @return string
     */
    static function build_query($formData, $numericPrefix = '', $argSeparator = '&', $prefixKey = '')
    {
        $str = '';
        foreach ($formData as $key => $val) {
            if (!is_array($val)) {
                $str .= $argSeparator;
                if ($prefixKey === '') {
                    if (is_int($key)) {
                        $str .= $numericPrefix;
                    }
                    $str .= urlencode($key) . '=' . urlencode($val);
                } else {
                    $str .= urlencode($prefixKey) . '=' . urlencode($val);
                }
            } else {
                if ($prefixKey == '') {
                    $prefixKey .= $key;
                }
                if (is_array($val[0])) {
                    $arr = array();
                    $arr[$key] = $val[0];
                    $str .= $argSeparator . http_build_query($arr);
                } else {
                    $str .= $argSeparator . self::build_query($val, $numericPrefix, $argSeparator, $prefixKey);
                }
                $prefixKey = '';
            }
        }
        return substr($str, strlen($argSeparator));
    }

    /**
     * http请求（协程版）
     */
    static function httpReq(
        $action,
        $params,
        $contentType = 'urlencoded',
        $httpMethod = 'POST',
        $headerOptions = []
    ) {
        $factory = new HandlerStackFactory();
        $stack = $factory->create();
        $client = make(Client::class, ['config' => [
            'handler' => $stack
        ]]);

        $options = [
            'timeout' => 30,
            'verify' => false, //不验证 https
        ];
        $httpHeader = [];
        if ($httpMethod == 'POST' && $contentType == 'urlencoded') {
            $httpHeader['Content-Type'] = 'application/x-www-form-urlencoded';
            $options['body'] = self::build_query($params);
        }
        if ($httpMethod == 'POST' && $contentType == 'json') {
            $options['json'] = $params;
        }
        if ($httpMethod == 'GET' && $contentType == 'urlencoded') {
            $action .= strpos($action, '?') === false ? '?' : '&';
            $action .= self::build_query($params);
        }
        // $httpHeader = $this->createGuzzleHttpHeader();
        $headerOptions && $httpHeader = array_merge($httpHeader, $headerOptions);
        $options['headers'] = $httpHeader;
        $response = $client->request($httpMethod, $action, $options);
        return $response->getBody()->getContents();
    }

    /**
     * Curl请求
     */
    static function curlRequest($url, $postData = array(), $json = true)
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        if ($postData) {
            curl_setopt($ch, CURLOPT_POST, 1);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
        }
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 0);
        $info = curl_getinfo($ch);
        $data = curl_exec($ch);
        curl_close($ch);
        return $data;
    }

    /**
     * 生成二维码图片
     */
    static function qrCode($value, $size = 6)
    {
        $errorCorrectionLevel = 'H';  //容错级别
        $matrixPointSize = $size;      //生成图片大小
        //生成二维码图片
        $filename = 'xxx.png';
        QRcode::png($value, $filename, $errorCorrectionLevel, $matrixPointSize, 2);
    }

    /**
     * 给原图打二维码
     */
    static function buildImageWithQrcode()
    {
        $QR = imagecreatefromstring(file_get_contents($QR));
        $logo = imagecreatefromstring(file_get_contents($logo));
        $QR_width = imagesx($QR); //二维码图片宽度
        $QR_height = imagesy($QR); //二维码图片高度
        $logo_width = imagesx($logo); //logo图片宽度
        $logo_height = imagesy($logo); //logo图片高度
        $logo_qr_width = $QR_width / 5;
        $scale = $logo_width / $logo_qr_width;
        $logo_qr_height = $logo_height / $scale;
        $from_width = ($QR_width - $logo_qr_width) / 2;
        //重新组合图片并调整大小
        imagecopyresampled(
            $QR,
            $logo,
            $from_width,
            $from_width,
            0,
            0,
            $logo_qr_width,
            $logo_qr_height,
            $logo_width,
            $logo_height
        );
    }

    /**
     * 计算key
     * @param array $arr
     * @return string
     */
    static function getListKey($keyArr = [])
    {
        sort($keyArr);
        $key = implode('-', $keyArr);
        return md5($key);
    }

    /**
     * 身份证信息认证
     */
    static function verifyIdCard($name = '', $cardNo = '')
    {
        if (!$name || !$cardNo) return ['result' => 0];
        $url = 'https://zid.market.alicloudapi.com/idcard/VerifyIdcardv2';
        $params = [
            'cardNo' => $cardNo,
            'realName' => $name
        ];
        $headOption['Authorization'] = 'APPCODE ' . config('aliyun.cardVrfAppCode');
        $result = self::httpReq($url, $params, 'urlencoded', 'GET', $headOption);
        $result = json_decode($result, true);
        if (
            $result['error_code'] == 0 &&
            isset($result['result']['isok']) &&
            $result['result']['isok']
        ) {
            return [
                'result' => 1,
                'card_info' => $result['result']['IdCardInfor']
            ];
        }
        return ['result' => 0];
    }

    /**
     * 是否成年
     */
    static function verifyIsGrownUp($birthday = '')
    {
        $age = DateUtil::dateDiff($birthday, date('Y-m-d'), 'Y');
        return $age >= 18;
    }

    /**
     * 是否超过45岁
     */
    static function verifyIsOverAge($birthday = '')
    {
        $age = DateUtil::dateDiff($birthday, date('Y-m-d'), 'Y');
        return $age >= env('HOST_VERIFY_AGE_MAX', 50);
    }

    /**
     * 随机浮点数
     */
    static function randFloat($min = 0, $max = 50)
    {
        return $min + round(mt_rand() / mt_getrandmax() * ($max - $min), 1);
    }

    /**
     * 检测是否合法用户ID（profile_id）
     */
    static function isProfileIdValid($str = '')
    {
        $rule = '/^[1-9]\d{6}$/';
        preg_match($rule, $str, $result);
        return empty($result) ? false : true;
    }

    /**
     * 下载远程文件到本地
     */
    static function downloadFile($remoteFileUrl = '', $saveFile = '')
    {
        if (!$remoteFileUrl || !$saveFile) return ['errcode' => -1, 'errmsg' => '参数缺失'];
        try {
            $client = new Client(['verify' => false]);
            $filePath = BASE_PATH . '/temp/' . $saveFile;
            $response = $client->get($remoteFileUrl, ['sink' => $filePath]);
            if (file_exists($filePath)) return ['errcode' => 0, 'file' => $filePath];
            return ['errcode' => -1, 'errmsg' => '保存失败'];
        } catch (\Exception $e) {
            log_info(__METHOD__ . "==Exception:" . $e->getMessage());
        }
        return ['errcode' => -2, 'errmsg' => '保存异常'];
    }

    /**
     * 创建AMQP生产者
     */
    static function produceAmqp(ProducerMessage $message, $delayMs = 0)
    {
        // $delayMs && $message->setDelayMs($delayMs);
        $producer = ApplicationContext::getContainer()->get(Producer::class);
        return $producer->produce($message);
    }

    /**
     * 异步消息投递（Redis）
     */
    static function pushRedisAsync($job, $delay = 0, $queue = 'default')
    {
        $driver = ApplicationContext::getContainer()->get(DriverFactory::class)->get($queue);
        return $driver->push($job, $delay);
    }

    /**
     * 是否临时屏蔽各种推送
     */
    static function checkTempExcluded($userId = 0, $funcType = SettingAppHidden::FUNC_PUSH)
    {
        // if (config('busi.online_tip_enable')) return false;
        //排除提醒
        if (UserExcludeTip::checkExclude($userId)) return true;

        //获取用户当前地址
        $city = Context::get('user_locate_city');


        //屏蔽特定地区
//        $limitHiddenCity = config('busi.limit_hidden_city');
//        $limitHiddenCity = empty($limitHiddenCity) ? [] : explode(',',$limitHiddenCity);
//        if(in_array($city,$limitHiddenCity)) return true;
//        if(!empty($cityHidden) && in_array($city,$limitHiddenCity)) return true;

        //屏蔽设置
        $settings = SettingAppHidden::getSetting($funcType);

        if (empty($settings)) return false;
        $user = User::getUser($userId);
        if (!$user) return true;

//        $userProfile = UserProfile::where(['user_id' => $user->id])->select(['user_id', 'rich_value'])->first();
        $userProfile = UserProfile::getProfile($userId);
        if (empty($userProfile)) {
            return true;
        }

        $userCity = $userProfile->locate_city;


        $appHiddenRichValue = env('APP_HIDDEN_RICH_VALUE', 5000);

        foreach ($settings as $setting) {
            //屏蔽特定地区
            $cityHidden = true;
            if (!empty($setting['hidden_city']) && $setting['hidden_un_know_city']) {
                if (!(in_array($userCity, $setting['hidden_city']) || empty($userCity))) {
                    $cityHidden = false;
                }
            } elseif (!empty($setting['hidden_city']) && $setting['hidden_un_know_city'] == 0) {
                if (!in_array($userCity, $setting['hidden_city'])) {
                    $cityHidden = false;
                }
            } elseif (empty($setting['hidden_city']) && $setting['hidden_un_know_city']) {
                if (!empty($userCity)) {
                    $cityHidden = false;
                }
            }

            if (
                in_array($user->cur_channel, $setting['channel'])
                && ($user->version == $setting['version'] || $user->app_version == $setting['version'])
                && ($userProfile->rich_value < $appHiddenRichValue)
                && $cityHidden
            ) {
                SettingAppHidden::addExcludeUserSet($userId);

                return true;
            }
        }

        return false;
    }

    /**
     * 微信排出
     */
    static function checkWechatExcluded($appId = 1, $channel = '', $version = '')
    {
        $settings = SettingAppHidden::getSetting(SettingAppHidden::FUNC_WECHAT);
        if (empty($settings)) return false;
        foreach ($settings as $setting) {
            if (
                // in_array($appId, $setting['app_id']) &&
                in_array($channel, $setting['channel'])
                && $version == $setting['version']
            ) return true;
        }
        return false;
    }

    /**
     * 提取城市
     */
    static function buildCity($city = '')
    {
        if (!$city) return '';
        $city = explode(' ', $city);
        return count($city) > 1 ? $city[1] : $city[0];
    }

    /**
     * 用户定位地址是否受限制
     */
    static function checkLocationLimited($locateCity = '')
    {
        if (!$locateCity) return false;
        $locateCity = trim($locateCity);
        if (strpos($locateCity, '市') === false) $locateCity .= '市';
        $limitedCities = explode(',', env('LIMITED_CITY', ''));
        return in_array($locateCity, $limitedCities);
    }

    /**
     * 用户定位地址是否受注册限制
     */
    static function checkLocationRegLimited($locateCity = '')
    {
        if (!$locateCity) return false;
        $locateCity = trim($locateCity);
        if (strpos($locateCity, '市') === false) $locateCity .= '市';
        $limitedCities = explode(',', env('LIMITED_REG_CITY', ''));
        return in_array($locateCity, $limitedCities);
    }

    /**
     * 通过身份证获取性别
     * @param $idcard
     * @return int 2-女 1-男
     */
    static function getSexByIdCard($idcard)
    {
        // 获取身份证倒数第二位数字
        $number = substr($idcard, strlen($idcard) - 2, 1);
        if ($number % 2 == 0) { // 偶数女
            return 2;
        } else { // 基数男
            return 1;
        }
    }
}
